/*
  FISCO BCOS/rust-SDK is a rust client for FISCO BCOS2.0 (https://github.com/FISCO-BCOS/)
  FISCO BCOS/rust-SDK is free software: you can redistribute it and/or modify it under the
  terms of the MIT License as published by the Free Software Foundation. This project is
  distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
  @author: kentzhang
  @date: 2021-07
*/
#![allow(
    clippy::unreadable_literal,
    clippy::upper_case_acronyms,
    dead_code,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    overflowing_literals,
    unused_imports,
    unused_results,
    unused_variables
)]
extern crate libloading;

use std::convert::From;
use std::env;
use std::path::Path;
use std::time::Duration;

use libc::{ c_void};
use libloading::{Library, Symbol};
use serde_json::{Value as JsonValue};

use crate::bcossdk::bcosclientconfig::{BcosCryptoKind, ChannelConfig, ClientConfig};
use crate::bcossdk::bufferqueue::BufferQueue;
use crate::bcossdk::channelpack::{ChannelPack, make_channel_pack};
use crate::bcossdk::commonutil::is_windows;
use crate::bcossdk::kisserror::{KissErrKind, KissError};

//C native api 类型定义
type FN_SSOCK_CREATE = fn() -> *mut ::libc::c_void;
type FN_SSOCK_FINISH = fn(*mut ::libc::c_void);
type FN_SSOCK_RELEASE = fn(*mut ::libc::c_void);
type FN_SSOCK_INIT = fn(p: *mut ::libc::c_void, ca: *const u8, sdk: *const u8, key: *const u8, ensdk: *const u8, enkey: *const u8) -> i32;
type FN_SSOCK_SET_ECHO_MODE = fn(*mut ::libc::c_void, i32);
type FUNC_SSOCK_TRY_CONNECT = fn(*mut c_void, host: *const u8, port: i32) -> i32;
type FUNC_SSOCK_SEND = fn(*mut c_void, buffer: *const u8, len: i32) -> i32;
type FUNC_SSOCK_RECV = fn(*mut c_void, buffer: *mut u8, size: i32) -> i32;

   pub fn lib_usage_msg() -> String{
        let msg:String =
            r###"
            此版本的tls链接依赖tassl动态库和c/c++原生代码实现，正常运行的注意点：
            1）所有符合当前操作系统的动态库齐备。
            2）动态库放于和目标运行程序相同的目录，或当前操作系统寻址加载动态库的目录。
            3）如有版本出入，可能需要重新编译tassl库和c/c++原生代码。
            具体参见“native_ssock_lib”的readme.md（本项目readme有连接）
            "###.to_string();

        msg
    }


///强行在String的 最后补'\0',以作为c风格字符串传给c库，否则会有乱字符串传进去
fn rzero(s: &String) -> String
{
    let mut n = s.clone();
    n.push('\0');
    n
}
#[derive( Debug)]
pub struct SSockNativeLib{
    pub pssock: *mut libc::c_void,
    pub nativelib: Library,
}
impl SSockNativeLib{
    pub fn close(&mut self){

    }
}
///tls 客户端，封装c动态库加载，c native api调用
#[derive( Debug)]
pub struct BcosTlsClient {
    pub ssocklib:Option<SSockNativeLib>,
    pub config:ChannelConfig,
    pub bufferqueue:BufferQueue,
    pub is_valid : bool,
    pub is_connect :bool,
    pub channelpackpool :Vec<ChannelPack> //一个池子，存没有被处理的channelpack，在推送等流程用到
}

impl BcosTlsClient {
    pub fn default(config:&ChannelConfig)->BcosTlsClient{
        BcosTlsClient{
            ssocklib: None,
            config: config.clone(),
            bufferqueue: BufferQueue::new(),
            is_valid: false,
            is_connect: false,
            channelpackpool: Vec::new()
        }
    }

    pub fn build(&mut self) -> Result<bool, KissError> {
        let res = BcosTlsClient::openlib();
        let nativelib = match res {
            Ok(lib) => { lib }
            Err(e) => { return Err(e); }
        };
        unsafe {
            let func_create: Symbol<FN_SSOCK_CREATE> = (&nativelib).get(b"ssock_create").unwrap();
            let pssock_ptr = func_create();
            let lib = SSockNativeLib{
                pssock :pssock_ptr,
                nativelib:  nativelib
            };
            self.ssocklib = Option::from(lib);
            self.set_echo_mode(self.config.nativelib_echo_mode as i32);
            self.init()?;
            self.connect()?;
            self.is_valid = true;
            self.is_connect = true;
            Ok(self.is_valid)
        }
    }
    pub fn finish(&mut self){
        self.release();
    }

    pub fn locate_lib_path() -> String
    {
            let mut exepath = env::current_exe().unwrap();
      //println!("curr exe path:{:?}",exefile.canonicalize().unwrap().as_path());
            exepath.pop();
            let mut libname:String = "native_tassl_sock_wrap".to_string();
            if is_windows(){
                libname = libname+ ".dll";
            }else{
                libname = libname + "so";
            }
            let fullpath = exepath.join(Path::new(libname.as_str()));
            //let pathstr =  fullpath.as_os_str().to_str().unwrap();
            let pathstr = libname;
            return pathstr.to_string()
    }
    ///寻找相应路径下的动态库
    pub fn openlib() -> Result<Library, KissError> {
        unsafe {
            let currpath = Path::new("../..");

            //println!("curr exe :{:?}", env::current_exe().unwrap());
            //println!("curr exe path:{:?}",exefile.as_path().to_str());
            //println!("curr dir: {:?}", currpath.canonicalize().unwrap());
            //let dllfile = currpath.join("native_tassl_sock_wrap.dll");

            let lib_fullpath = BcosTlsClient::locate_lib_path();
            //println!("lib file : {:?}", lib_fullpath);
            let res = Library::new(lib_fullpath.as_str());

            match res {
                Ok(lib)=>{return Ok(lib);},
                Err(e)=>{
                    return kisserr!(KissErrKind::Error,
                                              "load lib error [{}],{:?}",lib_fullpath,e);
                }
            }

        }
    }
    ///在堆上创建c底层的对象，类似 ssl_new分配了一个context，openssl也是同理创建了一个c指针对象保存了下来，
    /// todo：安全性，生命周期再做优化
    pub fn create(&self)
    {
        unsafe {
            let func_create: Symbol<FN_SSOCK_CREATE> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_create").unwrap();
            let pssock = func_create();
            // println!("{:?}",pssock);
        }
    }
    ///释放在堆上分配的内容
    pub fn release(&mut self) {
        if !self.is_valid {
            printlnex!("bcostls client is no valid");
            return
        };
        unsafe {
            let func_release: Symbol<FN_SSOCK_RELEASE> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_release").unwrap();
            func_release(self.ssocklib.as_ref().unwrap().pssock);
            //self.ssocklib.as_ref().unwrap().close();
            //self.ssocklib.as_ref().unwrap().pssock = ptr::null_mut();
            //self.ssocklib.unwrap().nativelib.close();
            self.ssocklib = Option::None;
            self.is_connect = false;
            self.is_valid = false;
            printlnex!("bcostls client is released!");
        }
    }

    ///设置底层api的打印级别，0，完全不打印，1打印，todo：在c底层支持日志
    pub fn set_echo_mode(&self, mode: i32)
    {
        // (self.nativelib.ssock_set_echo_mode)(self.ssocklib.as_ref().unwrap().pssock,mode);
        unsafe {
            let func_set: Symbol<FN_SSOCK_SET_ECHO_MODE> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_set_echo_mode").unwrap();
            func_set(self.ssocklib.as_ref().unwrap().pssock, mode);
            //println!("set echo mode:{:?},mode {}", self.ssocklib.as_ref().unwrap().pssock,mode);
        }
    }

    ///初始化，根据tlskind配置的不同，可以同时支持国密和ecdsa的连接类型，传入不同的证书即可
    pub fn init(&self) -> Result<i32, KissError>
    {
        unsafe {
            let func_init: Symbol<FN_SSOCK_INIT> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_init").unwrap();
            //let r =func_init(self.ssocklib.as_ref().unwrap().pssock,self.cacrt.as_str().as_ptr(),self.sdkcrt.as_str().as_ptr(),self.sdkkey.as_str().as_ptr(),"","");
            let r;
            match self.config.tlskind
            {
                BcosCryptoKind::ECDSA => {
                r = func_init(self.ssocklib.as_ref().unwrap().pssock,
                              rzero(&self.config.cacert).as_ptr(),
                              rzero(&self.config.sdkcert).as_ptr(),
                              rzero(&self.config.sdkkey).as_ptr(),
                              rzero(&"".to_string()).as_ptr(),
                              rzero(&"".to_string()).as_ptr());
                }
                BcosCryptoKind::GM =>{
                r = func_init(self.ssocklib.as_ref().unwrap().pssock,
                              rzero(&self.config.gmcacert).as_ptr(),
                              rzero(&self.config.gmsdkcert).as_ptr(),
                              rzero(&self.config.gmsdkkey).as_ptr(),
                              rzero(&self.config.gmensdkcert).as_ptr(),
                              rzero(&self.config.gmensdkkey).as_ptr());

                }

            }
            if r<0
            {
                return kisserr!(KissErrKind::ENetwork, "init tls client error {}", r)
            }
            Ok(r)

        }
    }

    ///tls连接，会发起握手
    pub fn connect(&self)->Result<i32, KissError>
    {
        unsafe {
            let func_connect: Symbol<FUNC_SSOCK_TRY_CONNECT> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_try_connect").unwrap();
            let r = func_connect(self.ssocklib.as_ref().unwrap().pssock, rzero(&self.config.ip).as_ptr(), self.config.port as i32);
            if r<0
            {
                return kisserr!(KissErrKind::ENetwork, "tls connect error {}", r)
            }
            Ok(r)
        }
    }

    ///异步发送数据，如果未发送任何字节，返回0，可以重试发送
    pub fn send(&self, sendbuff: &Vec<u8>) -> Result<i32, KissError> {
        unsafe {
            let func_send: Symbol<FUNC_SSOCK_SEND> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_send").unwrap();
            let r = func_send(self.ssocklib.as_ref().unwrap().pssock, sendbuff.as_ptr(), sendbuff.len() as i32);
            if r < 0
            {
                return kisserr!(KissErrKind::ENetwork, "send err {}",r);
            }
            Ok(r)
        }
    }

    ///读取，c api要求输入一个预先分配好的缓冲区，讲读取的信息写入缓冲区带回
    pub fn recv(&self) -> Result<Vec<u8>, KissError>
    {
        unsafe {
            let func_recv: Symbol<FUNC_SSOCK_SEND> = self.ssocklib.as_ref().unwrap().nativelib.get(b"ssock_recv").unwrap();
            let size:usize =1024*10;
            let mut recvbuffer: Vec<u8> = Vec::with_capacity( size );
            let pdst = recvbuffer.as_mut_ptr();
            //let dstlen = 1024;
            let r = func_recv(self.ssocklib.as_ref().unwrap().pssock, pdst, size as i32);
            //println!("recv result {:?}", r);
            if r >= 0
            {
                //println!("recv size :{}",r);

                recvbuffer.set_len(r as usize);

                //println!("buffer {:?}",recvbuffer);
                Ok(recvbuffer)
            }
            else {
                return kisserr!(KissErrKind::Error, "recv {}",r);
            }
        }
    }
    ///尝试最多5次异步发送
    pub fn try_send(&self,outbuffer:&Vec<u8>)->Result<i32, KissError>
    {
        let mut i=0;
        while i < 5 {
           let res = self.send(outbuffer)?;
           if res > 0 {
               return Ok(res);
            }
            std::thread::sleep(Duration::from_millis(50));
            i += 1;
        }
        kisserr!(KissErrKind::ENetwork, "send none bytes after try")
    }
    /// 按配置的超时时间读socket
    pub fn try_recv(&self)->Result<Vec<u8>, KissError>
    {
        let mut i=0;
        while i < self.config.timeout * 10 {
            let res = self.recv()?;
            if res.len() > 0 {
                return Ok(res);
            }
            i += 1;
            std::thread::sleep(Duration::from_millis(100));
        }
        kisserr!(KissErrKind::ENetwork, "recv time out")
    }

    ///传入json 字符串，打入channelpack发送出去，同步等待read，然后返回从channelpack解析好的value
    pub fn request_sync(&mut self, reqtext: &str) -> Result<String, KissError>
    {
        let outpack = make_channel_pack(reqtext).unwrap();
        let returnpack = self.request_channelpack_sync(&outpack)?;
        let res = String::from_utf8(returnpack.data);
        //println!("***** channel pack unpack sn {:?},type 0x{:02X}",&returnpack.seq,&returnpack.packtype);
        //println!("***** channel pack unpack {:?}",&res);
        match res {
            Ok(s)=>{return Ok(s)}
            Err(e)=>{return kisserr!(KissErrKind::ENetwork, "pack data is not string {:?}", e)}
        }
    }

   ///尝试从缓冲区获得一个对应的回包
   pub fn try_match_channelpack(&mut self,outpack:&ChannelPack)->Result<ChannelPack, KissError>
   {
       //从缓冲区中match出一个回包，最多尝试50次
       let mut i=0;
       let mut thepack:Option<ChannelPack> = Option::None ;
       //println!("want pack type: 0x{:02X},seq: {}",outpack.packtype,outpack.seq);

       while i<50 {
               // println!("before peek, queue size : {}",&self.bufferqueue.queue.len());
               if self.bufferqueue.queue.len() <=42
               {
                     break;
               }
                let packres = ChannelPack::unpack(&self.bufferqueue.queue);
                match packres {
                Ok(pack)=>{
                    //println!(">>>> get pack type: 0x{:02X},seq: {}",pack.packtype,pack.seq);
                    //从缓冲区中去掉已经解码的部分
                    self.bufferqueue.cut(pack.length);
                    //获得了一个回包，要判断下是否预期的回包，否则仅加入等待区

                    if pack.packtype == outpack.packtype && pack.seq == outpack.seq
                    {
                        //println!(">>>match pack type: 0x{:02X},seq: {}",pack.packtype,pack.seq);
                        //是想要的回包
                        thepack = Option::from(pack);
                    }else{
                        //println!("not match  pack type: 0x{:02X},seq: {}",pack.packtype,pack.seq);
                        //是个完整的pack，但不是想要的，放入pool
                        self.channelpackpool.push(pack);
                    }

                },
                Err(e)=>{
                    //从缓冲区中解码失败，通常原因是字节不够了，终止
                    //println!("no more data");
                    break;
                }
            }
           i+=1;
       }
       //返回发现的pack,或反馈一个空包错误
       match thepack
       {
           Some(pack)=>{Ok(pack)},
           _=>{kisserr!(KissErrKind::EAgain, "no pack found")}
       }

   }

    ///这个方法主要是保证首先数据能发送出去（重试几次），然后从网络读入数据，直到得到发送的pack所对应的type和seq的回包
    pub fn request_channelpack_sync(&mut self, outpack:&ChannelPack) -> Result<ChannelPack, KissError> {
        let outbuffer = outpack.pack();
        self.try_send(&outbuffer)?;
        let mut i = 0;
        while i < 50 {
            let mut res = self.try_recv()?;
            if res.len() == 0
            {
                continue;
            }
            //读到的所有的数据先加入buffer
            self.bufferqueue.append(&mut res);
            let matchres = self.try_match_channelpack(outpack);
            match matchres {
                Ok(pack)=>{
                    //获得了一个pack，返回即可
                    return Ok(pack);
                },
                _=>{ /*继续等待即可*/ }
            }
            i+=1;
        }
        //没有获得任何数据
        return kisserr!(KissErrKind::EAgain,
                                  "no data return");

    }

}

pub fn test_tls_client() {
    let config = ClientConfig::load("conf/client_config.toml").unwrap();
    let mut client = BcosTlsClient::default(&config.channel);
    //client.create();
    client.set_echo_mode(1);
    let res = client.init();
    println!("init {:?}",res);
    let res = client.connect();
    println!("connect {:?}",res);
    let reqtext = "{\"jsonrpc\":\"2.0\",\"method\":\"getClientVersion\",\"params\":[],\"id\":1}";

    let res = client.request_sync(reqtext).unwrap();
    println!("request respones str :{:?}",res);
    let jsonres :JsonValue = serde_json::from_str(res.as_str()).unwrap();
    println!("json result is {:?}",jsonres["result"]);

    client.release();
}

